/*
 * SecuentialSort.h
 *
 *  Created on: Nov 3, 2013
 *      Author: SG0214746
 */

#ifndef SECUENTIALSORT_H_
#define SECUENTIALSORT_H_

#define TEMP_EXT ".tmp"
#define TEMP_NAME "temporary"

#include <algorithm>
#include <iostream>
#include <iterator>
#include "SecuentialFile.h"
#include "SecuentialMerge.h"
#include "../../utils/Utils.h"

template <class T>
class SecuentialSort {
	public:
		SecuentialSort(int bufferSize, BigInt tempFilesBlockSize, int mergeLines);
		virtual ~SecuentialSort();

		void setTempName(const String& name);
		void setMergeName(const String& name);
		String sort(SecuentialFile<T>& file, Compare comparator);

	private:
		typedef std::vector<T*> Buffer;
		typedef typename Buffer::iterator BuffIterator;

		void initNewTempFile(SecuentialFile<T>& file);
		void loadBuffer(SecuentialFile<T>& file);
		void createTempFiles(SecuentialFile<T>& file, Compare comparator);

		String mergeName;
		String tempName;
		Buffer buffer;
		BigInt frozen;
		BigInt count;
		StrVector temps;
		BigInt tempFilesBlockSize;
		int mergeLines;
};

template <class T>
SecuentialSort<T>::SecuentialSort(int bufferSize, BigInt tempFilesBlockSize, int mergeLines) : buffer(bufferSize) {
	this->mergeLines = mergeLines;
	this->tempFilesBlockSize = tempFilesBlockSize;
	frozen = 0;
	count = 0;
	for (int x = 0; x < buffer.size(); x++) {
		buffer[x] = new T();
	}
	tempName = TEMP_NAME;
	mergeName = "";
}

template <class T>
SecuentialSort<T>::~SecuentialSort() {
	for (int x = 0; x < buffer.size(); x++) {
		delete buffer[x];
	}
}

template <class T>
void SecuentialSort<T>::loadBuffer(SecuentialFile<T>& file) {
	count = 0;
	while (count < buffer.size() && file.next(*buffer[count])) {
		++count;
	}
}

template <class T>
void SecuentialSort<T>::setTempName(const String& name) {
	tempName = name;
}

template <class T>
void SecuentialSort<T>::setMergeName(const String& name) {
	mergeName = name;
}

template <class T>
void SecuentialSort<T>::createTempFiles(SecuentialFile<T>& file, Compare comparator) {
	frozen = 0;
	SecuentialFile<T> temp(tempFilesBlockSize);
	initNewTempFile(temp);
	T* first;
	T* next = new T();
	bool continueLoop = true;
	while (continueLoop) {
		std::sort(&buffer[frozen], &buffer[count], comparator);
		first = buffer[frozen];
		if (file.next(*next)) {
			temp.add(*first);
			buffer[frozen] = next;
			next = first;
			if (comparator(buffer[frozen], first)) {
				frozen++;
				if (frozen == buffer.size()) {
					frozen = 0;
					temp.close();
					initNewTempFile(temp);
				}
			}
		}
		else {
			for (int x = frozen; x < count; x++) {
				temp.add(*buffer[x]);
			}
			temp.close();
			if (frozen > 0) {
				initNewTempFile(temp);
				std::sort(buffer.begin(), buffer.begin() + frozen, comparator);
				for (int x = 0; x < frozen; x++) {
					temp.add(*buffer[x]);
				}
				temp.close();
			}
			continueLoop = false;
		}
	}
	delete next;
}

template <class T>
String SecuentialSort<T>::sort(SecuentialFile<T>& file, Compare comparator) {
	file.rewind();
	loadBuffer(file);
	createTempFiles(file, comparator);
	if (temps.size() == 1) {
		return temps[0];
	}
	SecuentialMerge<T> merge(temps, tempFilesBlockSize, mergeLines);
	if (mergeName.length() > 0)
		merge.setMergeName(mergeName);
	return merge.merge(comparator);
}

template <class T>
void SecuentialSort<T>::initNewTempFile(SecuentialFile<T>& file) {
	String tempPath = tempName + Utils::toString(temps.size() + 1) + TEMP_EXT;
	temps.push_back(tempPath);
	file.create(tempPath);
}

#endif /* SECUENTIALSORT_H_ */
